﻿using Metabase;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml;

namespace ApplicationGUI
{
    static class ServiceProcs
    {
        // Процедура возвращает параметры, которые не храним вне приложения
        static private string getParamStrValue(ParamTypes paramType)
        {
            if (paramType == ParamTypes.FolderForSaveData) return "\\SmartBI\\DataAnalizer\\";
            if (paramType == ParamTypes.NodeNameFolder) return "folder";
            if (paramType == ParamTypes.NodeNameRepository) return "repository";
            if (paramType == ParamTypes.NodeNameRepoParams) return "repo_parameters";
            if (paramType == ParamTypes.NodeNameParamServer) return "server";
            if (paramType == ParamTypes.NodeNameParamDatabase) return "database";
            if (paramType == ParamTypes.NodeNameParamUser) return "user";
            if (paramType == ParamTypes.NodeNamePassword) return "pass";
            if (paramType == ParamTypes.NodeNameParamDomainAuth) return "domain_auth";
            if (paramType == ParamTypes.NodeNameParamSchema) return "schema";
            
            return "";
        }
        
        // Процедура записывает список репозиториев пользователя в Xml файл
        static public void SaveRepoListToXml(TreeView currentTree)
        {
            string directory = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + getParamStrValue(ParamTypes.FolderForSaveData);
            string fileName = directory  + currentTree.Name + ".xml";
            // Создадим директорию, если ее еще нет
            if (!System.IO.Directory.Exists(directory)) { System.IO.Directory.CreateDirectory(directory); }
            StreamWriter streamWriter = new StreamWriter(fileName, false, System.Text.Encoding.UTF8);
            //Запишем заголовок
            streamWriter.WriteLine("<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameFolder) + " name = \"" + currentTree.Nodes[0].Text + "\">");
            // Запишем структуру каталогов
            foreach (TreeNode node in currentTree.Nodes)
            {
                saveNode(node.Nodes, streamWriter);
            }
            streamWriter.WriteLine("</folder>");
            streamWriter.Close();
        }

        // Рекурсивная процедура записи нода
        static private void saveNode(TreeNodeCollection nodes, StreamWriter streamWriter)
        {
            foreach (TreeNode node in nodes)
            {
                string tagName;
                if ((node.Tag as NodeData).NodeType == NodeTypes.Folder) { tagName = getParamStrValue(ParamTypes.NodeNameFolder); }
                else { tagName = getParamStrValue(ParamTypes.NodeNameRepository); }

                if (node.Nodes.Count > 0)
                {
                    streamWriter.WriteLine("<" + tagName + " name = \"" + node.Text + "\">");
                    saveNode(node.Nodes, streamWriter);
                    streamWriter.WriteLine("</" + tagName + ">");
                }
                else
                {
                    streamWriter.WriteLine("<" + tagName + " name = \"" + node.Text + "\">");
                    // Если это репозиторий, то запишем его параметры
                    if ((node.Tag as NodeData).NodeType == NodeTypes.Repository)
                    {
                        SaveRepoParams(node.Tag as NodeData, streamWriter);
                    }
                    streamWriter.WriteLine("</" + tagName + ">");
                }
            }
        }

        // Процедура записи параметров репозитория
        static private void SaveRepoParams(NodeData nodeData, StreamWriter streamWriter)
        {
            streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameRepoParams) + ">");
            if (nodeData.dbHandler != null)
            {
                streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameParamServer) + ">" + nodeData.dbHandler.ServerName + "</" + getParamStrValue(ParamTypes.NodeNameParamServer) + ">");
                streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameParamDatabase) + ">" + nodeData.dbHandler.Database + "</" + getParamStrValue(ParamTypes.NodeNameParamDatabase) + ">");
                streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameParamUser) + ">" + nodeData.dbHandler.UserName + "</" + getParamStrValue(ParamTypes.NodeNameParamUser) + ">");
                streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameParamDomainAuth) + ">" + nodeData.dbHandler.DomainAuth + "</" + getParamStrValue(ParamTypes.NodeNameParamDomainAuth) + ">");
                streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNameParamSchema) + ">" + nodeData.dbHandler.Schema + "</" + getParamStrValue(ParamTypes.NodeNameParamSchema) + ">");
                using (var secureString = nodeData.dbHandler.Pwd.ToSecureString())
                {
                    streamWriter.WriteLine("<" + getParamStrValue(ParamTypes.NodeNamePassword) + ">" + secureString.EncryptString() + "</" + getParamStrValue(ParamTypes.NodeNamePassword) + ">");
                }
            }
            streamWriter.WriteLine("</" + getParamStrValue(ParamTypes.NodeNameRepoParams) + ">");
        }

        // Процедура восстанавливает из Xml файла список репозиториев пользователя
        static public void LoadRepoListFromXml(TreeView currentTree)
        {
            string fileName = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + getParamStrValue(ParamTypes.FolderForSaveData) + currentTree.Name + ".xml";
            // Проверим на наличие файла
            if (!System.IO.File.Exists(fileName)) { return; }
            XmlDocument xDoc = new XmlDocument();
            xDoc.Load(fileName);
            // Добавим базовый нод
            NodeData nodeData = new NodeData { NodeType = NodeTypes.BaseNode };
            TreeNode newNode = new TreeNode { Text = xDoc.DocumentElement.Attributes.GetNamedItem("name").Value, Tag = nodeData, ImageIndex = 0 };
            currentTree.Nodes.Add(newNode);
            // Соберем в рекурсии все ноды из файла
            addTreeNode(xDoc.DocumentElement, newNode);
        }

        // Рекурсивная процедура чтения нодов
        static private void addTreeNode(XmlNode xmlNode, TreeNode treeNode)
        {
            XmlNode xNode;
            XmlNodeList xNodeList;
            ImageIndexes imageIndex;
            ImageIndexes selectedImageIndex;
            if (xmlNode.HasChildNodes) 
            {
                xNodeList = xmlNode.ChildNodes;
                for (int x = 0; x <= xNodeList.Count - 1; x++)
                {
                    xNode = xmlNode.ChildNodes[x];
                    NodeData nodeData = new NodeData();
                    // Если это папка - создадим папку
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameFolder))
                    {
                        nodeData.NodeType = NodeTypes.Folder;
                        imageIndex = ImageIndexes.Folder;
                        selectedImageIndex = ImageIndexes.Folder;
                    }
                    else
                    {
                        // Если репозиторий - создадим репозиторий
                        if (xNode.Name == getParamStrValue(ParamTypes.NodeNameRepository))
                        {
                            {
                                nodeData.NodeType = NodeTypes.Repository;
                                imageIndex = ImageIndexes.Repository;
                                selectedImageIndex = ImageIndexes.Repository;
                            }
                        }
                        else
                        {
                            // Осальные случаи проигнорируем
                            continue;
                        }
                    }
                    TreeNode newNode = new TreeNode { Text = xNode.Attributes.GetNamedItem("name").Value, Tag = nodeData, ImageIndex = (int)imageIndex, SelectedImageIndex =  (int)selectedImageIndex};
                    // Если нодом является репозиторий, то загрузим его параметры
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameRepository))
                    {
                        (newNode.Tag as NodeData).dbHandler = LoadRepoParams(xNode);
                    }
                    treeNode.Nodes.Add(newNode);
                    addTreeNode(xNode, newNode);
                }
            }
        }

        // Процедура загрузки параметров нода
        static private DBHandler LoadRepoParams(XmlNode xmlNode)
        {
            DBHandler dbHandler = new DBHandler();
            XmlNode paramNode = xmlNode.SelectNodes(getParamStrValue(ParamTypes.NodeNameRepoParams)).Item(0);
            if (paramNode == null) return null;
            if (paramNode.HasChildNodes)
            {
                XmlNodeList xNodeList;
                XmlNode xNode;
                xNodeList = paramNode.ChildNodes;
                for (int x = 0; x <= xNodeList.Count - 1; x++)
                {
                    xNode = paramNode.ChildNodes[x];
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameParamServer)) dbHandler.ServerName = xNode.InnerText;
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameParamDatabase)) dbHandler.Database = xNode.InnerText;
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameParamUser)) dbHandler.UserName = xNode.InnerText;
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameParamSchema)) dbHandler.Schema = xNode.InnerText;
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNamePassword))
                    {
                        using (var secureString = xNode.InnerText.DecryptString())
                        {
                            dbHandler.Pwd = secureString.ToInsecureString();
                        }
                    }
                    if (xNode.Name == getParamStrValue(ParamTypes.NodeNameParamDomainAuth)) 
                    {
                        bool DomainAuth;
                        if (bool.TryParse(xNode.InnerText, out DomainAuth))
                        {
                            dbHandler.DomainAuth = DomainAuth;
                        }
                    }
                }
            }
            return dbHandler;
        }
    }

    // Служебные энумераторы

    // Тип нода в отображении списка репозиториев
    enum NodeTypes
    {
        BaseNode, Folder, Repository
    }
    // Индекс картинки для дерева - списка нодов
    enum ImageIndexes
    {
        Folder = 0, Repository = 1
    }
    // Тип параметра для получения значения из функции getParamStrValue
    enum ParamTypes
    {
        FolderForSaveData, NodeNameFolder, NodeNameRepository, NodeNameRepoParams, NodeNameParamServer, NodeNameParamDatabase, NodeNameParamUser, NodeNamePassword, NodeNameParamDomainAuth, NodeNameParamSchema
    }
}
